package com.crazy.jiakoulachang.aop;

import com.crazy.jiakoulachang.annotation.MyLog;
import com.crazy.jiakoulachang.common.Constant;
import com.crazy.jiakoulachang.exception.ExceptionEnum;
import com.crazy.jiakoulachang.exception.MallException;
import com.crazy.jiakoulachang.pojo.Role;
import com.crazy.jiakoulachang.pojo.SysLog;
import com.crazy.jiakoulachang.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;

/**
 * @author: lhy-卢辉宇
 * @date: 2023/6/21 20:35
 * @description: 自定义切面类 定义了通知和切点的关系
 */
@Component
@Aspect
@Slf4j
public class MyLogAspect {

    @Resource
    private SysLogService logService;

    /**
     * @param point 连接点
     * @param myLog 自定义注解切入点的形参
     * @return
     * @throws Throwable
     */
    @Around("@annotation(myLog)")//绑定切入点的形参，切点应用于我们自定义的注解类，即哪里用了这个注解，哪里就是切点
    public Object log(ProceedingJoinPoint point, MyLog myLog) throws Throwable {
        Long beginTime = System.currentTimeMillis();//记录开始时间

        //执行触发切面的方法（使用MyLog注解的方法）
        Object result = point.proceed();

        Long time = System.currentTimeMillis() - beginTime; //执行的时间差

        //保存日志
        saveLog(point, time);

        return result;
    }

    /**
     * 组织好数据并调用log的service进行保存到数据库
     *
     * @param joinPoint
     * @param time
     * @throws JsonProcessingException
     */
    private void saveLog(ProceedingJoinPoint joinPoint, Long time) throws JsonProcessingException {

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        //获取参数名数组
        String[] parameterNames = signature.getParameterNames();

        //使用获取参数数组
        Object[] array = joinPoint.getArgs();


        //使用ServletRequestAttributes请求上下文获取方法更多
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        log.info("获取自定义注解的module描述值：{}",method.getAnnotation(MyLog.class).module());
        log.info("获取自定义注解的operator描述值：{}",method.getAnnotation(MyLog.class).operator());
        log.info("方法名：{}",method.getName());
        log.info("参数名：{}",Arrays.toString(parameterNames));
        log.info("参数：{}", Arrays.toString(array));
        log.info("参数：{}",Arrays.toString(addParameterName(parameterNames,array)));
        log.info("请求的URL：{}", request.getRequestURL().toString());
        log.info("请求的远程ip：{}", request.getRemoteAddr());
        log.info("耗时：{}", time);


        //组织到log的实体类中
        SysLog log = new SysLog();
        //获取注解上的描述值
        MyLog annotation = method.getAnnotation(MyLog.class);
        if (!annotation.module().isEmpty()) {
            log.setModule(annotation.module());
        } else log.setModule("此接口日志注解未描述模块信息");
        if (!annotation.operator().isEmpty()) {
            log.setOperation(annotation.operator());
        } else log.setOperation("此接口日志注解未描述操作接口信息");

        //通过session保存的数据获取当前登录用户
        Object userObj = request.getSession().getAttribute(Constant.USER_LOGIN_STATE);
        Role currentUser = (Role) userObj;
        if (currentUser != null) {
            if(currentUser.getUserAccount() != null) {
                log.setUserAccount(currentUser.getUserAccount());
            }

            if(currentUser.getUserName() != null) {
                log.setUsername(currentUser.getUserName());
            }
        }

        log.setMethod(method.getName());
//        log.setParams(Arrays.toString(array));
        log.setParams(Arrays.toString(addParameterName(parameterNames,array)));
        log.setUrl(request.getRequestURL().toString());
        log.setTime(time);
        log.setIp(request.getRemoteAddr());
        System.out.println(log.getParams());

        //调用log的service进行数据库的保存
        logService.saveLog(log);
    }

    /**
     * 参数加上参数名返回String[]:
     * 即，原来形式：
     * 参数名：[userName, number, productType, weight, details]
     * 参数：[测试12, 12345678900, 腊肠, 500.0, null]
     *
     * 转化后：
     *参数：[userName=测试12, number=12345678900, productType=腊肠, weight=500.0, details=null]
     *
     * @param parameterNames  参数名：[userName, number, productType, weight, details]
     * @param array 参数：[测试12, 12345678900, 腊肠, 500.0, null]
     */
    private Object [] addParameterName(String[] parameterNames, Object[] array) {
        ArrayList<String> result = new ArrayList<>();
        for (int i=0;i<parameterNames.length;i++){
            String str= parameterNames[i] + "=" + array[i];
            result.add(str);
        }
        return result.toArray();
    }


}
